package org.dromara.tearoom.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.tearoom.config.GeeOpenConfig;
import org.dromara.tearoom.domain.enums.IotDeviceBreakerEnum;
import org.dromara.tearoom.domain.enums.IotDeviceOperTypeEnum;
import org.dromara.tearoom.mapper.TerIotDeviceMapper;
import org.dromara.tearoom.mqtt.BreakerDataRequest;
import org.dromara.tearoom.mqtt.BreakerFactory;
import org.dromara.tearoom.service.IBreakerCommandService;
import org.dromara.tearoom.service.IGeeOpenService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * GeeOpen服务
 * @author CKJ
 * @description: GeeOpen服务
 * @date 2025/4/10 17:39
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GeeOpenServiceImpl implements IGeeOpenService {

    private final RedisTemplate<String, String> redisTemplate;
    private final GeeOpenConfig geeOpenConfig;
    private final TerIotDeviceMapper terIotDeviceMapper;




    /**
     * 获取有效token（自动刷新）
     */
    public String getValidToken() {
        String token = redisTemplate.opsForValue().get(geeOpenConfig.getTokenKey());
        if (StringUtils.isEmpty(token)) {
            return refreshToken();
        }
        return token;
    }

    /**
     * 强制刷新token
     */
    @Synchronized
    public String refreshToken() {
        try {
            JSONObject loginBody = JSONUtil.createObj()
                    .set("username", geeOpenConfig.getUsername())
                    .set("password", geeOpenConfig.getPassword());

            HttpResponse response = HttpRequest.post(geeOpenConfig.getLoginUrl())
                    .header("Content-Type", "application/json")
                    .body(loginBody.toString())
                    .execute();

            if (response.isOk()) {
                JSONObject json = JSONUtil.parseObj(response.body());
                if(json.getInt("code")!=0){
                    log.error(json.getStr("message"));
                    throw new ServiceException(json.getStr("message"));
                }
                JSONObject data= JSONUtil.parseObj(json.getStr("data"));
                String newToken = data.getStr("access_token");
                // 存储到Redis并设置小于2小时过期
                redisTemplate.opsForValue().set(
                        geeOpenConfig.getTokenKey(),
                        newToken,
                        100,
                        TimeUnit.MINUTES
                );
                log.info("Token刷新成功:{}",newToken);
                return newToken;
            }
            throw new RuntimeException("登录失败: " + response.getStatus());
        } catch (Exception e) {
            log.error("Token刷新异常", e);
            throw new RuntimeException("Token刷新失败", e);
        }
    }

    private String sendCommand(String token,String deviceKey,JSONObject cmd,Integer retryCount) {
        JSONObject commandBody = JSONUtil.createObj()
                .set("clientId", deviceKey)
                .set("cmd", cmd.toString());

        HttpResponse response = HttpRequest.post(geeOpenConfig.getPubUrl())
                .header("Content-Type", "application/json")
                .header("Authorization", "bearer "+token)
                .body(commandBody.toString())
                .execute();

        if (response.isOk()) {
            log.info("命令发送成功：{}", response.body());
        } else {
            if (retryCount < 3) {
                log.error("命令发送失败：{}", response.body());
                redisTemplate.delete(geeOpenConfig.getTokenKey());
                sendCommand(getValidToken(), deviceKey, cmd,retryCount+1);
            }else {
                log.error("已达到最大重试次数3次，停止重试");
            }
        }
        return response.body();
    }

    /**
     * 控制开关
     *
     * @param deviceKey
     * @param state
     */
    @Override
    public void switchOffByHttp(String deviceKey, Integer state) {
        // 发送命令
        JSONObject cmd = JSONUtil.createObj().set("type", "event").set("key",state);
        sendCommand(getValidToken(),deviceKey,cmd,0);
    }

    /**
     * 控制开关
     *
     * @param mqttSubscribe
     * @param state
     */
    @Override
    public void switchOffByMqtt(String mqttSubscribe, Integer state) {
        // 发送命令
        IBreakerCommandService breakerCommandService = BreakerFactory.getBreakerCommandService(IotDeviceBreakerEnum.GeeOpen);
        BreakerDataRequest breakerDataRequest = new BreakerDataRequest();
        breakerDataRequest.setMqttSubscribe(mqttSubscribe);
        JSONObject cmd = JSONUtil.createObj().set("type", "event").set("key",state);
        breakerDataRequest.setCommand(cmd.toString());
        breakerDataRequest.setOperaType(IotDeviceOperTypeEnum.OPERA_KEY.getType());
        breakerCommandService.commandBreaker(breakerDataRequest,false,true);
    }

    /**
     * 获取设备信息
     *
     * @param mqttSubscribe
     */
    @Override
    public void getDeviceInfoByMqtt(String mqttSubscribe) {
        // 发送命令
        IBreakerCommandService breakerCommandService = BreakerFactory.getBreakerCommandService(IotDeviceBreakerEnum.GeeOpen);
        BreakerDataRequest breakerDataRequest = new BreakerDataRequest();
        breakerDataRequest.setMqttSubscribe(mqttSubscribe);
        JSONObject cmd = JSONUtil.createObj().set("type", "info");
        breakerDataRequest.setCommand(cmd.toString());
        breakerDataRequest.setOperaType(IotDeviceOperTypeEnum.GET_INFO.getType());
        breakerCommandService.commandBreaker(breakerDataRequest,false,true);
    }

    /**
     * 获取空气开关的状态信息
     *
     * @param deviceKey
     * @return
     */
    @Override
    public JSONObject getAirSwitchInfo(String deviceKey) {
        // 发送命令
        JSONObject cmd = JSONUtil.createObj().set("type", "info");
        String result=sendCommand(getValidToken(),deviceKey,cmd,0);
        return JSONUtil.parseObj(result);
    }
}
